using System;
using System.Collections.Generic;
using System.IO;

namespace pal.SvnConf.Core
{
    //Understands the reading from and writing to an SVN passwd file
    public class PasswdFile : IConfigurationFile
    {
        private const char SECTION_NAME_PREFIX = '[';
        private const char SECTION_NAME_SUFFIX = ']';
        private const string USERS_SECTION_NAME = "users";
        private TextReader _textReader;
        private List<User> _users = new List<User>();
        //State pattern?
        private bool _parsingUsersSection = false;
        private char SPACE = ' ';

        public PasswdFile(TextReader textReader)
        {
            _textReader = textReader;
        }

        public int Count
        {
            get { return _users.Count; }
        }

        public List<User> Users
        {
            get { return _users; }
        }

        public User this[int index]
        {
            get { return _users[index]; }
        }

        public void Parse()
        {
            do
            {
                string line = _textReader.ReadLine();
                if (isUsersSectionStart(line))
                {
                    _parsingUsersSection = true;
                    continue;
                }

                if (_parsingUsersSection)
                    _users.Add(User.Parse(line));

                if (isUsersSectionEnd(line))
                {
                    _parsingUsersSection = false;
                    break;
                }
            } while (streamHasMoreEntries());
            _textReader.Close();
        }

        private bool isUsersSectionEnd(string line)
        {
            return isSectionStart(line) && !isUsersSection(line);
        }

        private bool isUsersSection(string line)
        {
            return sectionName(line).Equals(USERS_SECTION_NAME);
        }

        private bool isUsersSectionStart(string line)
        {
            return isSectionStart(line) && isUsersSection(line);
        }

        private string sectionName(string line)
        {
            return line.Trim(SECTION_NAME_PREFIX, SECTION_NAME_SUFFIX, SPACE);
        }

        private bool isSectionStart(string line)
        {
            return line.StartsWith(SECTION_NAME_PREFIX.ToString());
        }

        private bool streamHasMoreEntries()
        {
            return _textReader.Peek() != -1;
        }

        public void Persist(TextWriter textWriter)
        {
            startSection(textWriter);
            foreach (User user in _users)
                writeUser(textWriter, user);
        }

        private void writeUser(TextWriter writer, User user)
        {
            writer.WriteLine(string.Format("{0} = {1}", user.Name, user.Password));
        }

        private void startSection(TextWriter textWriter)
        {
            textWriter.WriteLine(SECTION_NAME_PREFIX + USERS_SECTION_NAME + SECTION_NAME_SUFFIX);
        }

        public User FindUserByName(string userName)
        {
            return _users.Find(delegate(User current) { return current.Name.Equals(userName.Trim()); });
        }

        public void AddNewUser(string userName, string password)
        {
            if (FindUserByName(userName) != null)
                throw new ApplicationException(
                    string.Format("User '{0}' already present. Can not create duplicate user.", userName));
            _users.Add(new User(userName, password));
        }

        public void DeleteUser(string userName)
        {
            User userToDelete = FindUserByName(userName);
            if (userToDelete == null)
                throw new ApplicationException(string.Format("Couldn't find user: '{0}'", userName));
            _users.Remove(userToDelete);
        }
    }
}